package query

import (
	"bytes"
	"crypto/sha1"
	"encoding/gob"
	"errors"
	"fmt"
	"shqsoft.cn/pasnj/base"
	"shqsoft.cn/pasnj/bi/cube"
	"shqsoft.cn/pasnj/bi/datapackage"
	"strconv"
	"strings"
	"time"
)

//CacheDuration 查询缓存过期时间
const CacheDuration = time.Hour * 8

var cacheRedisCli = base.RedisClients[0]

var cellsMap map[string]map[string]*float64

//CreateResultMap 根据请求生成结果集
func CreateResultMap(req *Request) (*Response, error) {
	cacheObj := make([]string, len(req.Context)+2)
	if req.Row == nil {
		return nil, errors.New("必须指定行！")
	}
	cacheObj = append(cacheObj, req.Row.Hierarchy)
	if req.Row != nil {
		cacheObj = append(cacheObj, req.Column.Hierarchy)
	}
	cacheObj = append(cacheObj, req.Context...)
	buf := bytes.NewBuffer(nil)
	enc := gob.NewEncoder(buf)
	err := enc.Encode(cacheObj)
	if err != nil {
		return nil, err
	}
	h := sha1.New()
	h.Write(buf.Bytes())
	bs := h.Sum(nil)
	cacheKey := fmt.Sprintf("%x", bs)
	redisCli := cacheRedisCli
	lock := cacheKey + ":lock"
	rowLev, rowName, err := parseAxis(req.Row)
	if err != nil {
		return nil, err
	}
	colLev, colName, err := parseAxis(req.Column)
	if err != nil {
		return nil, err
	}
	for {
		find, err := redisCli.Exists(lock).Result()
		if err != nil {
			return nil, fmt.Errorf("执行createResultMap时，调用redis命令Exists出错！%s", err)
		}
		if find == 0 {
			ok, err := redisCli.SetNX(lock, "1", CacheDuration).Result()
			if err != nil {
				return nil, err
			}
			if !ok {
				time.Sleep(time.Second)
			} else {
				txid := 0
				dimName := rowLev.GetDimensionName()
				if dimName == "对公客户" {
					txid = 1
				} else if dimName == "零售客户" {
					txid = 2
				}
				context, err := parseContext(req.Context)
				if err != nil {
					return nil, err
				}
				if err := collectData(cacheKey, rowLev, rowName, colLev, colName, context, txid); err != nil {
					return nil, err
				}
				break
			}
		} else {
			break
		}
	}
	columns, colKeys, err := createColumns(req, cacheKey, colLev)
	if err != nil {
		return nil, err
	}
	if err := rowLev.FilterAndSort(cacheKey, true); err != nil {
		return nil, err
	}
	allRowKeys, err := rowLev.GetKeys()
	if err != nil {
		return nil, err
	}
	count := len(allRowKeys)
	start := (req.PageIndex - 1) * req.PageLineNum
	end := req.PageLineNum * req.PageIndex
	if end > count {
		end = count
	}
	rowKeys := allRowKeys[start:end]
	if err != nil {
		return nil, err
	}
	rkLen := len(rowKeys)
	datas := make([]map[string]interface{}, rkLen, rkLen)
	for i, rowKey := range rowKeys {
		dMap, err := getRowData(rowKey, colKeys, cacheKey, req.Measures, req.UnitBase)
		if err != nil {
			return nil, err
		}
		kv, err := rowLev.GetNameByKey(rowKey, req.Row.Properties...)
		if err != nil {
			return nil, err
		}
		for pk, pv := range kv.Props {
			dMap[pk] = pv
		}
		dMap["$key"] = kv.Key
		dMap[rowLev.GetName()] = kv.Value
		datas[i] = dMap
	}
	res := &Response{
		Columns:  columns,
		DataList: datas,
		Total:    int(count),
	}
	return res, nil
}

func parseContext(context []string) (keyMap map[string][]string, err error) {
	keyMap = make(map[string][]string)
	for _, item := range context {
		dimName, keys, err := parseContextItem(item)
		if err != nil {
			return nil, err
		}
		keyMap[dimName] = keys
	}
	return
}

func parseContextItem(item string) (dimName string, keys []string, err error) {
	item = strings.TrimSpace(item)
	if strings.HasPrefix(item, "{") {
		if strings.HasSuffix(item, "}") {
			item = strings.TrimLeft(item, "{")
			item = strings.TrimRight(item, "}")
			if strings.Index(item, ":") != -1 {
				return parseRangeItem(item)
			} else if strings.Index(item, ",") != -1 {
				return parseMultiItem(item)
			} else {
				return parseSingleItem(item)
			}
		} else {
			return "", nil, fmt.Errorf("集合表达式：%s无效！", item)
		}
	} else {
		return parseSingleItem(item)
	}
}
func parseSingleItem(item string) (dimName string, keys []string, err error) {
	mItems := strings.Split(item, ".")
	if len(mItems) != 4 {
		return "", nil, fmt.Errorf("成员表达式：%s无效！", mItems)
	}
	lkey := mItems[3]
	lev, name, err := cube.GetLevel(item)
	if err != nil {
		return "", nil, err
	}
	dimName = name
	keys, err = lev.GetLeafKeys(lkey)
	return
}
func parseMultiItem(item string) (dimName string, keys []string, err error) {
	mems := strings.Split(item, ",")
	mItems1 := strings.Split(mems[0], ".")
	lkeys := make([]string, 0)
	lkeys = append(keys, mItems1[3])
	if len(mItems1) != 4 {
		return "", nil, fmt.Errorf("成员表达式：%s无效！", mItems1)
	}
	for i := 1; i < len(mems); i++ {
		mItemsX := strings.Split(mems[i], ".")
		if len(mItemsX) != 4 {
			return "", nil, fmt.Errorf("成员表达式：%s无效！", mItemsX)
		}
		if mItems1[0] != mItemsX[0] || mItems1[1] != mItemsX[1] || mItems1[2] != mItemsX[2] {
			return "", nil, fmt.Errorf("集合表达式：%s无效,给定的成员不属于同一层次结构！", item)
		}
		lkeys = append(keys, mItems1[4])
	}
	lev, name, err := cube.GetLevel(mems[0])
	if err != nil {
		return "", nil, err
	}
	dimName = name
	keys, err = lev.GetLeafKeys(lkeys...)
	return
}
func parseRangeItem(item string) (dimName string, keys []string, err error) {
	mems := strings.Split(item, ":")
	if len(mems) != 2 {
		return "", nil, fmt.Errorf("集合表达式：%s无效！", item)
	}
	mItems1 := strings.Split(mems[0], ".")
	mItems2 := strings.Split(mems[1], ".")
	if len(mItems1) != 4 {
		return "", nil, fmt.Errorf("成员表达式：%s无效！", mems[0])
	}
	if len(mItems2) != 4 {
		return "", nil, fmt.Errorf("成员表达式：%s无效！", mems[1])
	}
	if mItems1[0] != mItems2[0] || mItems1[1] != mItems2[1] || mItems1[2] != mItems2[2] {
		return "", nil, fmt.Errorf("集合表达式：%s无效,给定的成员不属于同一层次结构！", item)
	}
	lev, name, err := cube.GetLevel(mems[0])
	if err != nil {
		return "", nil, err
	}
	dimName = name
	lkeys, err := lev.GetRange(mItems1[3], mItems2[3])
	if err != nil {
		return "", nil, err
	}
	keys, err = lev.GetLeafKeys(lkeys...)
	return
}
func parseAxis(axis *Axis) (*cube.Level, string, error) {
	if axis == nil || axis.Hierarchy == "" {
		return nil, "", nil
	}
	lev, dimName, err := cube.GetLevel(axis.Hierarchy)
	if err != nil {
		return nil, "", err
	}
	for _, filter := range axis.Filters {
		switch strings.ToLower(filter.Type) {
		case "label":
			lev.SetFilterByLabel(filter.Operator, filter.Value)
		case "property":
			lev.SetFilterByProperty(filter.Name, filter.Operator, filter.Value)
		case "measure":
			lev.SetFilterByMeasure(filter.Name, filter.Operator, filter.Value, filter.Context)
		}
		if err != nil {
			return nil, "", err
		}
	}
	for _, sort := range axis.Sorts {
		switch strings.ToLower(sort.Type) {
		case "label":
			lev.SetSortByLabel(sort.Order)
		case "property":
			lev.SetSortByProperty(sort.Column, sort.Order)
		case "measure":
			lev.SetSortByMeasure(sort.Column, sort.Order, sort.Context)
		}
		if err != nil {
			return nil, "", err
		}
	}
	return lev, dimName, nil
}

func collectData(cacheKey string, rowLev *cube.Level, rowName string, colLev *cube.Level, colName string, context map[string][]string, txid int) error {
	if rowName == "" {
		return errors.New("行上必须指定维度！")
	}
	redisCli := cacheRedisCli
	rowKeys, err := rowLev.GetKeys()
	if err != nil {
		return err
	}
	var colKeys []string
	if colLev != nil {
		colKeys, err = colLev.GetKeys()
		if err != nil {
			return err
		}
	}
	for kc := range context {
		if kc == rowName {
			return fmt.Errorf("维度：%s同时出现在列和上下文中，这不允许！", kc)
		}
		if kc == colName {
			return fmt.Errorf("维度：%s同时出现在列和上下文中，这不允许！", kc)
		}
	}
	dp := datapackage.DefaultDP
	var rowIndex, colIndex = -1, -1
	for i, dim := range dp.Dimensions {
		if dim.Name == rowName {
			rowIndex = i
		}
		if colName != "" && dim.Name == colName {
			colIndex = i
		}
	}
	if rowIndex == -1 {
		return fmt.Errorf("行上指定的维度%s不存在！", rowName)
	}
	if colKeys == nil || len(colKeys) == 0 {
		colIndex = -1
	}
	if colIndex == -1 {
		colKeys = []string{""}
	}
	var buff bytes.Buffer
	for _, rowK := range rowKeys {
		for _, colK := range colKeys {
			buff.Reset()
			buff.WriteString(rowK)
			rpKeys, err := rowLev.GetLeafKeys(rowK)
			if err != nil {
				return err
			}
			context[rowName] = rpKeys
			if colIndex != -1 {
				buff.WriteString(":")
				buff.WriteString(colK)
				cpKeys, err := colLev.GetLeafKeys(colK)
				if err != nil {
					return err
				}
				context[colName] = cpKeys
			}
			cellsRowKey := buff.String()
			valmap, err := aggregateAllIndex(context, txid)
			if err != nil {
				return err
			}
			cellsMap[cellsRowKey] = valmap
		}
	}
	if err := redisCli.RPush("cache:res", cacheKey).Err(); err != nil {
		return fmt.Errorf("执行collectData时，调用Redis命令RPush出错!,%s", err)
	}
	if err := redisCli.Expire("cache:res", CacheDuration).Err(); err != nil {
		return fmt.Errorf("执行collectData时，调用Redis命令Expire出错！%s", err)
	}
	return nil
}
func aggregateAllIndex(context map[string][]string, txid int) (map[string]*float64, error) {
	indexMap := GetIndexMap()
	valMap := make(map[string]*float64)
	for k, ind := range indexMap {
		if txid != 0 && ind.txid != txid {
			continue
		}
		val, err := GetIndexValue(k, context)
		if err != nil {
			return nil, err
		}
		if val != nil {
			valMap[k] = val
		}
	}
	return valMap, nil
}

func getRowData(rowKey string, colKeys []string, cacheKey string, meas []string, unitBase int) (map[string]interface{}, error) {
	hkey := cacheKey + ":h:" + rowKey
	redisCli := cacheRedisCli
	meaMap := make(map[string]interface{})
	if colKeys == nil {
		meaDatas, err := redisCli.HMGet(hkey, meas...).Result()
		if err != nil {
			return nil, err
		}
		for i, mea := range meas {
			meaMap[mea] = base.FormatValue(unitBase, meaDatas[i])
		}
	} else {
		for _, colKey := range colKeys {
			hkey += ":" + colKey
			meaDatas, err := redisCli.HMGet(hkey, meas...).Result()
			if err != nil {
				return nil, err
			}
			for i, mea := range meas {
				if meaDatas[i] != nil {
					meaMap[mea] = base.FormatValue(unitBase, meaDatas[i])
				}
			}
		}
	}
	return meaMap, nil
}
func createColumns(req *Request, cacheKey string, colLev *cube.Level) ([]Column, []string, error) {
	var colKeys []string
	var err error
	if req.Column != nil && req.Column.Hierarchy != "" {
		if err := colLev.FilterAndSort(cacheKey, false); err != nil {
			return nil, nil, err
		}
		colKeys, err = colLev.GetKeys()
		if err != nil {
			return nil, nil, fmt.Errorf("createColumns时出错！%s", err)
		}
	}
	columns := make([]Column, 0)
	for i, prop := range req.Row.Properties {
		columns = append(columns, Column{
			Title:     prop,
			DataIndex: prop,
			Key:       "p" + strconv.Itoa(i),
			Type:      "property",
			Fixed:     "left",
		})
	}
	rowLev, _, err := cube.GetLevel(req.Row.Hierarchy)
	if err != nil {
		return nil, nil, err
	}
	columns = append(columns, Column{
		Title:     rowLev.GetName(),
		DataIndex: rowLev.GetName(),
		DataType:  "string",
		Key:       req.Row.Hierarchy,
		Width:     120,
		Type:      "label",
		Fixed:     "left",
	})
	if colKeys != nil {
		lev, _, err := cube.GetLevel(req.Column.Hierarchy)
		if err != nil {
			return nil, nil, err
		}
		kvs, err := lev.GetNamesByKeys(colKeys)
		if err != nil {
			return nil, nil, err
		}
		for _, kv := range kvs {
			columns = append(columns, Column{
				Title: base.ToString(kv.Value),
				Key:   kv.Key,
			})
		}
	} else {
		kvs, err := GetNamesByIDs(req.Measures...)
		if err != nil {
			return nil, nil, err
		}
		kvLen := len(kvs)
		cols := make([]Column, kvLen, kvLen)
		for i, pkv := range kvs {
			cLen := len(pkv.Children)
			cols[i].Key = pkv.Key
			cols[i].Title = base.ToString(pkv.Value)
			cols[i].DataIndex = pkv.Key
			cols[i].Children = make([]Column, cLen, cLen)
			for j, kv := range pkv.Children {
				cols[i].Children[j].Key = kv.Key
				cols[i].Children[j].Title = base.ToString(kv.Value)
				cols[i].Children[j].DataIndex = kv.Key
				cols[i].Children[j].Width = 90
				cols[i].Children[j].DataType = "number"
				cols[i].Children[j].Type = "measure"
			}
		}
		columns = append(columns, cols...)
	}
	return columns, colKeys, nil
}
